import {
  _decorator,
  Animation,
  AudioClip,
  Component,
  director,
  find,
  instantiate,
  Label,
  math,
  Node,
  Prefab,
} from 'cc'
import { AudioMgr } from '../AudioMgr'
import { CHANGE_BOARD, CHECK_CLEAR, CHECK_COMPLETE, CHECK_LOSED } from '../Event'
import {
  GAME_BOARD_ENUM,
  GAME_EVENT_ENUM,
  GAME_SCENE_ENUM,
  GAME_STATUS_ENUM,
} from '../Enum'
import { DataManager } from '../DataManager'
import { Levels } from '../Levels'
import { shuffleArray } from '../Utils'
import { BlockType } from '../type'
import { Block } from './Block'
const { ccclass, property } = _decorator

@ccclass('GameSceneManager')
export class GameSceneManager extends Component {
  // 背景音
  @property({ type: AudioClip })
  mainBgm: AudioClip = null
  // 消除音效
  @property({ type: AudioClip })
  clearAudio: AudioClip = null
  // 完成音效
  @property({ type: AudioClip })
  completeAudio: AudioClip = null
  // 失败音效
  @property({ type: AudioClip })
  loseAudio: AudioClip = null
  // 按钮点击音效
  @property({ type: AudioClip })
  clickButton: AudioClip = null
  // 棋盘层级区域
  @property(Node)
  boardLevelNode: Node = null
  // 棋盘扩展区域
  @property(Node)
  boardLevelExtendNode: Node = null
  // 左随机区
  @property(Node)
  boardRandomLeftNode: Node = null
  // 右随机区
  @property(Node)
  boardRandomRightNode: Node = null
  // 插槽区
  @property(Node)
  boardSlotNode: Node = null
  // 隐藏区
  @property(Node)
  boardHideNode: Node = null
  // 块预制体
  @property(Prefab)
  blockPrefab: Prefab = null
  // 标题
  @property(Label)
  titleLabel: Label = null
  // 游戏结束节点
  @property(Node)
  gameOverNode: Node = null
  // 游戏完成节点
  @property(Node)
  gameCompleteNode: Node = null

  start() {
    // AudioMgr.inst.play(this.mainBgm, 0.5)
  }
  protected onLoad(): void {
    director.preloadScene(GAME_SCENE_ENUM.MENU)
    CHANGE_BOARD.on(GAME_EVENT_ENUM.CHANGE_BOARD, this.onBoardChange, this)
    CHECK_CLEAR.on(GAME_EVENT_ENUM.CHECK_CLEAR, this.onCheckClear, this)
    CHECK_LOSED.on(GAME_EVENT_ENUM.CHECK_LOSED, this.onCheckLosed, this)
    CHECK_COMPLETE.on(GAME_EVENT_ENUM.CHECK_COMPLETE, this.onCheckComplete, this)
    this.gameStart()
  }
  protected onDestroy(): void {
    CHANGE_BOARD.off(GAME_EVENT_ENUM.CHANGE_BOARD, this.onBoardChange, this)
    CHECK_CLEAR.off(GAME_EVENT_ENUM.CHECK_CLEAR, this.onCheckClear, this)
    CHECK_LOSED.off(GAME_EVENT_ENUM.CHECK_LOSED, this.onCheckLosed, this)
    CHECK_COMPLETE.off(GAME_EVENT_ENUM.CHECK_COMPLETE, this.onCheckComplete, this)
  }
  // 开始游戏
  gameStart() {
    this.clearNodeHistory()
    // 读档
    DataManager.inst.reStore()
    this.initGame()
    // 游戏状态
    DataManager.inst.gameStatus = GAME_STATUS_ENUM.RUNNING
  }
  // 初始化游戏
  initGame() {
    // 关卡
    const { level } = DataManager.inst
    DataManager.inst.reset()
    const currentLevel = Levels[level - 1]
    // 设置当前关卡数据
    DataManager.inst.currentLevel = currentLevel
    // 更新标题以显示当前关卡号
    this.titleLabel.string = `第${level}关`
    // 计算总块数totalBlockNum
    // 被清除块的最小单位
    // 确保生成的块数能被该单位整除
    const blockMinUnit = currentLevel.clearNum * currentLevel.blockTypeNum
    // 总块数
    let totalBlockNum =
      currentLevel.leftRandomBlocks +
      currentLevel.rightRandomBlocks +
      // 层数 * 每层数值
      currentLevel.layerNum * currentLevel.layerBlockNum
    // 根据最小单位修正，保证总块数能被最小单位整除
    if (totalBlockNum % blockMinUnit !== 0) {
      // 保证最小1个单位
      totalBlockNum = (Math.floor(totalBlockNum / blockMinUnit) + 1) * blockMinUnit
    }
    // 获取当前关卡有哪些块
    const currentLevelBlockTypes = currentLevel.blockTypeArr.slice(
      0,
      currentLevel.blockTypeNum,
    )
    // 块类型
    let typeArr: number[] = []
    // 根据总块数和当前关卡的块类型循环，循环填充typeArr，确保每个块类型被均匀分布在总块数中
    for (let i = 0; i < totalBlockNum; i++) {
      typeArr.push(currentLevelBlockTypes[i % currentLevel.blockTypeNum])
    }
    // 块类型乱序
    const shuffleTypeArr = shuffleArray(typeArr)
    // 根据乱序后的数组添加块
    const totalBlockArr: BlockType[] = []
    for (let i = 0; i < shuffleTypeArr.length; i++) {
      totalBlockArr.push({
        id: i,
        x: null,
        y: null,
        width: currentLevel.chessItemWidth * 3,
        height: currentLevel.chessItemHeight * 3,
        layer: 0,
        boardType: null,
        type: shuffleTypeArr[i],
        coverIds: [],
        beCoverIds: [],
      })
    }
    // 当前正在处理的块下标
    let currentHandleBlockIndex = 0
    // 初始化随机区域中块
    // 生成左侧块
    for (let i = 0; i < currentLevel.leftRandomBlocks; i++) {
      const node = instantiate(this.blockPrefab)
      node.setParent(this.boardRandomLeftNode)
      totalBlockArr[currentHandleBlockIndex].boardType = GAME_BOARD_ENUM.RANDOM_LEFT
      totalBlockArr[currentHandleBlockIndex].x -= i * 5
      totalBlockArr[currentHandleBlockIndex].y = 0
      totalBlockArr[currentHandleBlockIndex].layer -= i + 10
      // 堆叠关系
      let pre = currentHandleBlockIndex - 1
      while (pre >= 0 && totalBlockArr[pre].boardType === GAME_BOARD_ENUM.RANDOM_LEFT) {
        totalBlockArr[currentHandleBlockIndex].beCoverIds.push(totalBlockArr[pre].id)
        totalBlockArr[pre].coverIds.push(totalBlockArr[currentHandleBlockIndex].id)
        pre--
      }
      let next = currentHandleBlockIndex + 1
      while (
        next < currentLevel.leftRandomBlocks &&
        totalBlockArr[next].boardType === GAME_BOARD_ENUM.RANDOM_LEFT
      ) {
        totalBlockArr[currentHandleBlockIndex].beCoverIds.push(totalBlockArr[next].id)
        totalBlockArr[next].coverIds.push(totalBlockArr[currentHandleBlockIndex].id)
        next++
      }
      node.getComponent(Block).init(totalBlockArr[currentHandleBlockIndex])
      currentHandleBlockIndex++
    }
    // 生成右侧块
    for (let i = 0; i < currentLevel.rightRandomBlocks; i++) {
      const node = instantiate(this.blockPrefab)
      node.setParent(this.boardRandomRightNode)
      totalBlockArr[currentHandleBlockIndex].boardType = GAME_BOARD_ENUM.RANDOM_RIGHT
      totalBlockArr[currentHandleBlockIndex].x += i * 5
      totalBlockArr[currentHandleBlockIndex].y = 0
      totalBlockArr[currentHandleBlockIndex].layer -= i + 10
      // 堆叠关系
      let pre = currentHandleBlockIndex - 1
      while (
        pre >= currentLevel.leftRandomBlocks &&
        totalBlockArr[pre].boardType === GAME_BOARD_ENUM.RANDOM_RIGHT
      ) {
        totalBlockArr[currentHandleBlockIndex].beCoverIds.push(totalBlockArr[pre].id)
        totalBlockArr[pre].coverIds.push(totalBlockArr[currentHandleBlockIndex].id)
        pre--
      }
      let next = currentHandleBlockIndex + 1
      while (
        next < currentLevel.leftRandomBlocks + currentLevel.rightRandomBlocks &&
        totalBlockArr[next].boardType === GAME_BOARD_ENUM.RANDOM_RIGHT
      ) {
        totalBlockArr[currentHandleBlockIndex].beCoverIds.push(totalBlockArr[next].id)
        totalBlockArr[next].coverIds.push(totalBlockArr[currentHandleBlockIndex].id)
        next++
      }
      node.getComponent(Block).init(totalBlockArr[currentHandleBlockIndex])
      currentHandleBlockIndex++
    }
    // 初始化棋盘
    const chessBox = this.initBoard(
      currentLevel.chessWidthNum,
      currentLevel.chessHeightNum,
    )
    const chessBlocks: BlockType[] = []
    // 计算中间面板中的块数量
    let remaionBlockNum =
      totalBlockNum - currentLevel.leftRandomBlocks - currentLevel.rightRandomBlocks
    // 利用剩下的块按层组合
    let minWidth = 0,
      maxWidth = currentLevel.chessWidthNum - 2
    let minHeight = 0,
      maxHeight = currentLevel.chessHeightNum - 2
    for (let layerIndex = 0; layerIndex < currentLevel.layerNum; layerIndex++) {
      // 当前层块数量从剩余数量和当前层块数量取最小值
      let currentLayerBlockNum = Math.min(currentLevel.layerBlockNum, remaionBlockNum)
      // 最后一层的数量为剩余数量
      if (layerIndex === currentLevel.layerNum - 1) {
        currentLayerBlockNum = remaionBlockNum
      }
      // 更新剩余数量
      remaionBlockNum -= currentLayerBlockNum
      // 边界收缩处理
      // 块步长 > 0 且 非第一层
      if (currentLevel.layerContractStep > 0 && layerIndex > 0) {
        // 4个方向
        switch (layerIndex % 4) {
          case 0:
            minWidth += currentLevel.layerContractStep
            break
          case 1:
            maxWidth -= currentLevel.layerContractStep
            break
          case 2:
            maxHeight += currentLevel.layerContractStep
            break
          case 3:
            minHeight -= currentLevel.layerContractStep
            break
        }
      }
      // 获取当前层的块
      const currentLayerBlocks = totalBlockArr.slice(
        currentHandleBlockIndex,
        currentHandleBlockIndex + currentLayerBlockNum,
      )
      currentHandleBlockIndex += currentLayerBlockNum

      // 生成块坐标
      const blockPos = new Set<string>()
      // 遍历当前层块，为每个块生成位置坐标
      for (
        let currentLayerBlockIndex = 0;
        currentLayerBlockIndex < currentLayerBlocks.length;
        currentLayerBlockIndex++
      ) {
        const currentLayerBlock = currentLayerBlocks[currentLayerBlockIndex]
        let nx: number, ny: number, key: string
        // 根据边界情况生成随机块
        // 生成一个在最小宽度和最大宽度范围内的随机整数
        // 这用于在指定的范围内随机选择一个整数，以满足某些需要随机性的场景
        nx = Math.floor(Math.random() * (maxWidth + 1)) - maxWidth / 2
        ny = Math.floor(Math.random() * (maxHeight + 1)) - maxHeight / 2
        key = `${nx},${ny}`
        // 如果出现重叠，则重新生成
        if (blockPos.has(key)) {
          currentLayerBlockIndex--
          continue
        }
        blockPos.add(key)
        chessBox?.[nx]?.[ny]?.blocks?.push(currentLayerBlock)

        // 堆叠关系
        // const minX = Math.max(nx - 2, 0)
        // const minY = Math.max(ny - 2, 0)
        // const maxX = Math.min(nx + 2, maxWidth)
        // const maxY = Math.min(ny + 2, maxHeight)
        // let maxLayer = 0
        // for (let x = minX; x <= maxX; x++) {
        //   for (let y = minY; y <= maxY; y++) {
        //     // 临近块
        //     const nearlyBlocks = chessBox[x][y].blocks
        //     if (nearlyBlocks.length > 0) {
        //       // 当前最高层块
        //       const currentLayerTopestBlock = nearlyBlocks[nearlyBlocks.length - 1]
        //       if (currentLayerTopestBlock.id === currentLayerBlock.id) continue
        //       // 由下至上逐渐生成
        //       maxLayer = Math.max(maxLayer, currentLayerTopestBlock.layer)
        //       // 新生成的块覆盖当前最上层块
        //       currentLayerBlock.coverIds.push(currentLayerTopestBlock.id)
        //       // 同步修改被覆盖的块
        //       currentLayerTopestBlock.beCoverIds.push(currentLayerBlock.id)
        //     }
        //   }
        // }
        // currentLayerBlock.layer = maxLayer + 1
        currentLayerBlock.boardType = GAME_BOARD_ENUM.LEVEL
        currentLayerBlock.x = nx * currentLevel.chessItemWidth
        currentLayerBlock.y = ny * currentLevel.chessItemHeight
      }
      chessBlocks.push(...currentLayerBlocks)
    }
    // 棋盘区进行渲染
    chessBlocks.forEach(block => {
      const node = instantiate(this.blockPrefab)
      node.getComponent(Block).init(block)
    })
  }
  // 初始化棋盘方法
  initBoard(width: number, height: number) {
    const box = new Array(width)
    for (let i = 0; i < width; i++) {
      box[i] = new Array(height)
      for (let j = 0; j < height; j++) {
        box[i][j] = { blocks: [] }
      }
    }
    return box
  }
  // 点击块时切换状态
  onBoardChange(block: Block) {
    let board = this.boardLevelNode
    if (block.boardType === GAME_BOARD_ENUM.SLOT) {
      board = this.boardSlotNode
    }
    if (block.boardType === GAME_BOARD_ENUM.RANDOM_LEFT) {
      board = this.boardRandomLeftNode
    }
    if (block.boardType === GAME_BOARD_ENUM.RANDOM_RIGHT) {
      board = this.boardRandomRightNode
    }
    if (block.boardType === GAME_BOARD_ENUM.LEVEL_EXTEND) {
      board = this.boardLevelExtendNode
    }
    if (block.boardType === GAME_BOARD_ENUM.HIDE) {
      board = this.boardHideNode
    }
    block.node.setParent(board)
  }
  // 检查消除逻辑
  onCheckClear(block: Block) {
    // 在slot中的块
    const slotBlocks: Block[] = DataManager.inst.blocks.filter(
      block => block.boardType === GAME_BOARD_ENUM.SLOT,
    )
    // 在slot中相同的块
    const slotSameBlocks: Block[] = slotBlocks.filter(
      slotBlock => slotBlock.type === block.type,
    )
    // 相同块数量等于消除的数量
    if (slotSameBlocks.length === DataManager.inst.currentLevel.clearNum) {
      AudioMgr.inst.playOneShot(this.clearAudio)
      // 执行隐藏
      slotSameBlocks.forEach(block => {
        // 从DataManager中移除
        DataManager.inst.blocks.splice(DataManager.inst.blocks.indexOf(block), 1)
        // 注册动画
        // 获取目标block动画组件
        const anim = block.node.getComponent(Animation)
        anim.play(anim.clips[0].name)
        // 动画结束后，判断游戏是否结束
        this.scheduleOnce(() => {
          block.boardType = GAME_BOARD_ENUM.HIDE
          block.render()
          DataManager.inst.gameStatus = GAME_STATUS_ENUM.RUNNING
        }, anim.clips[0].duration)
      })
    }
    // 插槽已满且插槽内无可消除的块
    if (
      slotSameBlocks.length !== DataManager.inst.currentLevel.clearNum &&
      slotBlocks.length === DataManager.inst.currentLevel.slotNum
    ) {
      DataManager.inst.gameStatus = GAME_STATUS_ENUM.LOSED
      CHECK_LOSED.emit(GAME_EVENT_ENUM.CHECK_LOSED, this)
    }
    // 判断所有块是否都消除
    if (DataManager.inst.blocks.length === 0) {
      DataManager.inst.gameStatus = GAME_STATUS_ENUM.COMPLETE
      CHECK_COMPLETE.emit(GAME_EVENT_ENUM.CHECK_COMPLETE, this)
    }
  }
  // #region 技能区
  // 移除，放入扩展区
  onGameExtend() {
    // todo
  }
  // 撤销
  onGameUndo() {
    // todo
  }
  // 洗牌
  onGameShuffle() {
    // todo
  }
  // 明牌
  onGameOpen() {
    // todo
  }
  // #endregion
  // 游戏失败
  onCheckLosed() {
    AudioMgr.inst.playOneShot(this.loseAudio)
    DataManager.inst.gameStatus = GAME_STATUS_ENUM.LOSED
    this.gameOverNode.active = true
  }
  // 游戏成功
  onCheckComplete() {
    AudioMgr.inst.playOneShot(this.completeAudio)
    DataManager.inst.gameStatus = GAME_STATUS_ENUM.COMPLETE
    this.gameCompleteNode.active = true
    if (DataManager.inst.level === Levels.length) {
      find('Canvas/GameOverComplete/Panel/Restart').active = true
    } else {
      find('Canvas/GameOverComplete/Panel/Next').active = true
    }
  }
  // 下一关
  onGameNext() {
    AudioMgr.inst.playOneShot(this.clickButton)
    this.gameCompleteNode.active = false
    this.gameOverNode.active = false
    DataManager.inst.level += 1
    DataManager.inst.reset()
    this.gameStart()
  }
  // 重新开始游戏
  onGameRestart() {
    AudioMgr.inst.playOneShot(this.clickButton)
    this.gameCompleteNode.active = false
    this.gameOverNode.active = false
    DataManager.inst.level = 1
    DataManager.inst.reset()
    this.gameStart()
  }
  // 重新开始后清除节点
  clearNodeHistory() {
    this.boardLevelNode.removeAllChildren()
    this.boardLevelExtendNode.removeAllChildren()
    this.boardRandomLeftNode.removeAllChildren()
    this.boardRandomRightNode.removeAllChildren()
    this.boardSlotNode.removeAllChildren()
    this.boardHideNode.removeAllChildren()
  }
  // 返回按钮
  onBack() {
    director.loadScene(GAME_SCENE_ENUM.MENU)
  }
}
